package com.voxel.sense.infomsg.widget.map.cluster;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.LruCache;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.TextView;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.animation.AlphaAnimation;
import com.amap.api.maps.model.animation.Animation;
import com.example.com.common.utils.L;
import com.voxel.sense.infomsg.R;
import com.voxel.sense.infomsg.interfaces.listener.onAddMarkerListener;
import com.voxel.sense.infomsg.utils.marker.cluster.data.ClusterItem;

import org.greenrobot.eventbus.EventBus;

import java.util.ArrayList;
import java.util.List;

import cyy.example.com.facotry.Factory;
import cyy.example.com.facotry.constants.Constants;
import cyy.example.com.facotry.constants.EventBusConstants;
import cyy.example.com.facotry.model.EventEntity;


/**
 * 整体设计采用了两个线程,一个线程用于计算组织聚合数据,一个线程负责处理Marker相关操作
 */
public class ClusterOverlay implements AMap.OnCameraChangeListener,
        AMap.OnMarkerClickListener {
    private AMap mAMap;
    private Context mContext;
    private List<ClusterItem> mClusterItems; //真实的数据
    private List<Cluster> mClusters;//地图显示的marker点
    private List<Marker> mAddMarkers = new ArrayList<Marker>();
    private LruCache<Integer, BitmapDescriptor> mLruCache;
    private ClusterRender mClusterRender;
    private HandlerThread mMarkerHandlerThread = new HandlerThread("addMarker");
    private HandlerThread mSignClusterThread = new HandlerThread("calculateCluster");
    private Handler mMarkerhandler;
    private Handler mSignClusterHandler;
    private ClusterClickListener mClusterClickListener; //marker点击监听
    private onAddMarkerListener mAddMarkerListener;
    private LatLngBounds.Builder boundsBuilder;//显示区域

    private int mClusterSize;
    private double mClusterDistance;
    private float mPXInMeters;
    private boolean mIsCanceled = false;  //是否取消聚合计算
    private boolean mIsVisible = true;

    public List<ClusterItem> getClusterItems() {
        return mClusterItems;
    }

    /**
     * 构造函数
     *
     * @param amap        地图实例
     * @param clusterSize 聚合范围的大小（指点像素单位距离内的点会聚合到一个点显示）
     * @param context     上下文
     */
    public ClusterOverlay(AMap amap, int clusterSize, Context context) {
        this(amap, null, clusterSize, context);
    }

    /**
     * 构造函数,批量添加聚合元素时,调用此构造函数
     *
     * @param amap         地图实例
     * @param clusterItems 聚合元素
     * @param clusterSize  聚合范围的大小（指点像素单位距离内的点会聚合到一个点显示）
     * @param context      上下文
     */
    public ClusterOverlay(AMap amap, List<ClusterItem> clusterItems,
                          int clusterSize, Context context) {
//默认最多会缓存80张图片作为聚合显示元素图片,根据自己显示需求和app使用内存情况,可以修改数量
        mLruCache = new LruCache<Integer, BitmapDescriptor>(80) {
            protected void entryRemoved(boolean evicted, Integer key, BitmapDescriptor oldValue, BitmapDescriptor newValue) {
                if (oldValue.getBitmap() != null)
                    oldValue.getBitmap().recycle();
            }
        };
        if (clusterItems != null) {
            mClusterItems = clusterItems;

        } else {
            mClusterItems = new ArrayList<>();
        }
        L.d("ClusterOverlay----------公司的总数量:" + mClusterItems.size() + "----------mClusterItems");
        boundsBuilder = new LatLngBounds.Builder();
        mContext = context;
        mClusters = new ArrayList<>();
        this.mAMap = amap;
        mClusterSize = clusterSize;
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        amap.setOnMarkerClickListener(this);
        initThreadHandler();
        assignClusters();
    }

    /**
     * 地图改变事件自己处理
     */
    public void initCameraChangeEvent() {
        mAMap.setOnCameraChangeListener(this);
    }

    /**
     * 改变事件外部处理
     */
    public void setCameraChangeEvent(AMap.OnCameraChangeListener listener) {
        mAMap.setOnCameraChangeListener(listener);
    }

    /**
     * 设置聚合点的点击事件
     *
     * @param clusterClickListener 监听事件
     */
    public void setOnClusterClickListener(
            ClusterClickListener clusterClickListener) {
        mClusterClickListener = clusterClickListener;
    }

    /**
     * 添加一个聚合点
     */
    public void addClusterItem(ClusterItem item) {
        Message message = Message.obtain();
        message.what = SignClusterHandler.CALCULATE_SINGLE_CLUSTER;
        message.obj = item;
        mSignClusterHandler.sendMessage(message);
    }

    /**
     * 添加多个点
     */
    public void addClusterItems(List<ClusterItem> datas) {
        if (datas == null || datas.size() < 1)
            return;
        for (ClusterItem item : datas) {
            addClusterItem(item);
        }
    }

    /**
     * 设置聚合元素的渲染样式，不设置则默认为气泡加数字形式进行渲染
     *
     * @param render 样式事件
     */
    public void setClusterRenderer(ClusterRender render) {
        mClusterRender = render;
    }

    public void onDestroy() {
        mIsCanceled = true;
        mSignClusterHandler.removeCallbacksAndMessages(null);
        mMarkerhandler.removeCallbacksAndMessages(null);
        mSignClusterThread.quit();
        mMarkerHandlerThread.quit();
        for (Marker marker : mAddMarkers) {
            marker.remove();
        }
        mAddMarkers.clear();
        mLruCache.evictAll();
    }

    //初始化Handler
    private void initThreadHandler() {
        mMarkerHandlerThread.start();
        mSignClusterThread.start();
        mMarkerhandler = new MarkerHandler(mMarkerHandlerThread.getLooper());
        mSignClusterHandler = new SignClusterHandler(mSignClusterThread.getLooper());
    }

    @Override
    public void onCameraChange(CameraPosition arg0) {


    }

    /**
     * 在地图状态改变完成时回调此方法。
     */
    @Override
    public void onCameraChangeFinish(CameraPosition arg0) {
            updateClusters();
            EventBus.getDefault().post(new EventEntity(EventBusConstants.MAP_OCAMERA_CHANGE_FINISH, Constants.SUCCEED));
    }


    //刷新聚合点数据
    public void updateClusters() {
        mPXInMeters = mAMap.getScalePerPixel();
        mClusterDistance = mPXInMeters * mClusterSize;
        assignClusters();
    }

    //点击事件
    @Override
    public boolean onMarkerClick(Marker arg0) {
        return mClusterClickListener != null && mClusterClickListener.onClick(arg0);
    }

    /**
     * 替换数据源
     *
     * @param clusterItems
     */
    public void replaceCluster(List<ClusterItem> clusterItems) {
        if (clusterItems != null) {
            mClusterItems = clusterItems;
        } else {
            mClusterItems = new ArrayList<>();
        }
        assignClusters();
    }

    public List<Marker> getMarkers() {
        return mAddMarkers;
    }


    /**
     * 将聚合元素添加至地图上
     */
    private void addClusterToMap(List<Cluster> clusters) {

        ArrayList<Marker> removeMarkers = new ArrayList<>();
        removeMarkers.addAll(mAddMarkers);
        AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);
        MyAnimationListener myAnimationListener = new MyAnimationListener(removeMarkers);
        for (Marker marker : removeMarkers) {
            marker.setAnimation(alphaAnimation);
            marker.setAnimationListener(myAnimationListener);
            marker.startAnimation();
        }

        for (Cluster cluster : clusters) {
            addSingleClusterToMap(cluster);
        }
    }

    private AlphaAnimation mADDAnimation = new AlphaAnimation(0, 1);

    /**
     * 将单个聚合元素添加至地图显示
     */
    private void addSingleClusterToMap(Cluster cluster) {
        LatLng latlng = cluster.getCenterLatLng();
        MarkerOptions markerOptions = new MarkerOptions();
        ClusterItem clusterItem = cluster.getClusterItems().get(0);
        String markerType = clusterItem.getMarkerType();
        markerOptions.anchor(0.5f, 0.5f)
                .icon(getBitmapDes(cluster.getClusterCount(), markerType, clusterItem.getObject())).position(latlng);
        Marker marker = mAMap.addMarker(markerOptions);
        marker.setAnimation(mADDAnimation);
        if (!TextUtils.isEmpty(markerType)) {
            marker.setTitle(markerType);
        }
        if (cluster.getClusterItems().size() > 1) {
            //聚合点携带聚合信息
            marker.setObject(cluster);
        } else {
            //普通marker点，携带本身的信息
            marker.setObject(clusterItem);
        }
        if (mAddMarkerListener != null) {
            mAddMarkerListener.onAddMarkerLisener(marker);
        }
        marker.setVisible(mIsVisible);
        marker.startAnimation();
        cluster.setMarker(marker);
        mAddMarkers.add(marker);
    }


    private void calculateClusters() {
        mIsCanceled = false;
        mClusters.clear();

        //地图获取转换器, 获取可视区域, 获取可视区域的四个点形成的经纬度范围, 得到一个经纬度范围,该区域内的点，才会列入计算并显示。
        //不然界面刷新会比较慢
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        for (ClusterItem clusterItem : mClusterItems) {
            if (mIsCanceled) {
                return;
            }
            LatLng latlng = clusterItem.getPosition();//聚合元素的地理位置
            //该marker点是可见的才添加且地图的可视范围包含了该坐标
            if (clusterItem.getVisible() && visibleBounds.contains(latlng)) {
                Cluster cluster = getCluster(latlng, mClusters);//根据这个位置和聚合物的集合, 获得一个聚合器
                //存放所有的点的经纬度，用以做为地图的显示区域，显示所有的点
                if (cluster != null) {
                    cluster.addClusterItem(clusterItem);//往聚合器中添加聚合点
                } else {
                    cluster = new Cluster(latlng);
                    mClusters.add(cluster);
                    cluster.addClusterItem(clusterItem);
                }
            }
        }

        //复制一份数据，规避同步
        List<Cluster> clusters = new ArrayList<>();
        clusters.addAll(mClusters);
        Message message = Message.obtain();
        message.what = MarkerHandler.ADD_CLUSTER_LIST;
        message.obj = clusters;
        if (mIsCanceled) {
            return;
        }
        mMarkerhandler.sendMessage(message);
    }

    /**
     * 对点进行聚合
     */
    private void assignClusters() {
        mIsCanceled = true;
        mSignClusterHandler.removeMessages(SignClusterHandler.CALCULATE_CLUSTER);
        mSignClusterHandler.sendEmptyMessage(SignClusterHandler.CALCULATE_CLUSTER);
    }

    /**
     * 在已有的聚合基础上，对添加的单个元素进行聚合
     */
    private void calculateSingleCluster(ClusterItem clusterItem) {
        LatLngBounds visibleBounds = mAMap.getProjection().getVisibleRegion().latLngBounds;
        LatLng latlng = clusterItem.getPosition();
//        if (!visibleBounds.contains(latlng)) {
//            return;
//        }
        if (!clusterItem.getVisible())
            return;

        Cluster cluster = getCluster(latlng, mClusters);
        if (cluster != null) {
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.UPDATE_SINGLE_CLUSTER;
            message.obj = cluster;
            mMarkerhandler.removeMessages(MarkerHandler.UPDATE_SINGLE_CLUSTER);
            mMarkerhandler.sendMessageDelayed(message, 5);
        } else {
            cluster = new Cluster(latlng);
            mClusters.add(cluster);
            cluster.addClusterItem(clusterItem);
            Message message = Message.obtain();
            message.what = MarkerHandler.ADD_SINGLE_CLUSTER;
            message.obj = cluster;
            mMarkerhandler.sendMessage(message);

        }
    }

    /**
     * 根据一个点获取是否可以依附的聚合点，没有则返回null
     */
    private Cluster getCluster(LatLng latLng, List<Cluster> clusters) {
        for (Cluster cluster : clusters) {
            LatLng clusterCenterPoint = cluster.getCenterLatLng();
            double distance = AMapUtils.calculateLineDistance(latLng, clusterCenterPoint);
            if (distance < mClusterDistance && mAMap.getCameraPosition().zoom < Constants.MAP_MAX_ZOOM_LEVEL) {
                return cluster;
            }
        }

        return null;
    }


    /**
     * 获取每个聚合点的绘制样式
     */
    private BitmapDescriptor getBitmapDes(int num, String type, Object data) {
        BitmapDescriptor bitmapDescriptor = mLruCache.get(num);
        if (num != 1) {
            if (bitmapDescriptor == null) {
                TextView textView = new TextView(mContext);
                if (num > 1) {
                    String tile = String.valueOf(num);
                    textView.setText(tile);
                }
                textView.setGravity(Gravity.CENTER);
                textView.setTextColor(Color.BLACK);
                textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
                if (mClusterRender != null && mClusterRender.getDrawAble(num, type, data) != null) {
                    textView.setBackgroundDrawable(mClusterRender.getDrawAble(num, type, data));
                } else {
                    textView.setBackgroundResource(R.mipmap.a_level_firm);
                }
                bitmapDescriptor = BitmapDescriptorFactory.fromView(textView);
                mLruCache.put(num, bitmapDescriptor);
            }
        } else {
            Bitmap bitmap;
            if (mClusterRender != null && mClusterRender.getDrawAble(num, type, data) != null) {
                Drawable drawAble = mClusterRender.getDrawAble(num, type, data);
                bitmap = ((BitmapDrawable) drawAble).getBitmap();
            } else {
                //默认图标
                bitmap = BitmapFactory.decodeResource(Factory.app().getResources(), R.mipmap.ic_marker_firm);
            }
            bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap);
        }
        return bitmapDescriptor;
    }

    /**
     * 更新已加入地图聚合点的样式
     */
    private void updateCluster(Cluster cluster) {

        Marker marker = cluster.getMarker();
        marker.setIcon(getBitmapDes(cluster.getClusterCount(), marker.getTitle(), marker.getObject()));
    }


    private void alterVisibleState(List<Marker> list) {
        for (Marker marker : list) {
            marker.setVisible(mIsVisible);
        }
    }

    public boolean isVisible() {
        return mIsVisible;
    }

    public void setVisible(boolean visible) {
        mIsVisible = visible;
        Message obtain = Message.obtain();
        List list = new ArrayList();
        list.addAll(mAddMarkers);
        obtain.obj = list;
        obtain.what = MarkerHandler.SET_VISIBLE;
        mMarkerhandler.sendMessage(obtain);
    }

    public onAddMarkerListener getAddMarkerListener() {
        return mAddMarkerListener;
    }

    public void setAddMarkerListener(onAddMarkerListener addMarkerListener) {
        mAddMarkerListener = addMarkerListener;
    }

    //-----------------------辅助内部类用---------------------------------------------

    /**
     * marker渐变动画，动画结束后将Marker删除
     */
    class MyAnimationListener implements Animation.AnimationListener {
        private List<Marker> mRemoveMarkers;

        MyAnimationListener(List<Marker> removeMarkers) {
            mRemoveMarkers = removeMarkers;
        }

        @Override
        public void onAnimationStart() {

        }

        @Override
        public void onAnimationEnd() {
            for (Marker marker : mRemoveMarkers) {
                marker.remove();
            }
            mRemoveMarkers.clear();
        }
    }

    /**
     * 处理market添加，更新等操作
     */
    class MarkerHandler extends Handler {

        static final int ADD_CLUSTER_LIST = 0;

        static final int ADD_SINGLE_CLUSTER = 1;

        static final int UPDATE_SINGLE_CLUSTER = 2;

        static final int SET_VISIBLE = 3;

        MarkerHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {

            switch (message.what) {
                case ADD_CLUSTER_LIST:
                    List<Cluster> clusters = (List<Cluster>) message.obj;
                    addClusterToMap(clusters);
                    break;
                case ADD_SINGLE_CLUSTER:
                    Cluster cluster = (Cluster) message.obj;
                    addSingleClusterToMap(cluster);
                    break;
                case UPDATE_SINGLE_CLUSTER:
                    Cluster updateCluster = (Cluster) message.obj;
                    updateCluster(updateCluster);
                    break;
                case SET_VISIBLE:
                    List<Marker> list = (List<Marker>) message.obj;
                    alterVisibleState(list);
                    break;
            }
        }
    }

    /**
     * 处理聚合点算法线程
     */
    class SignClusterHandler extends Handler {
        static final int CALCULATE_CLUSTER = 0;
        static final int CALCULATE_SINGLE_CLUSTER = 1;

        SignClusterHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message message) {
            switch (message.what) {
                case CALCULATE_CLUSTER:
                    calculateClusters();
                    break;
                case CALCULATE_SINGLE_CLUSTER://增加元素消息
                    ClusterItem item = (ClusterItem) message.obj;
                    mClusterItems.add(item);
//                    Log.i("yiyi.qi", "calculate single cluster");
                    calculateSingleCluster(item);
                    break;
            }
        }
    }
}